﻿#region License
/*
 * Microsoft Public License (Ms-PL)
 * 
 * This license governs use of the accompanying software. If you use the software, you accept this license. 
 * If you do not accept the license, do not use the software.
 *
 * 1. Definitions
 *
 * The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
 *
 * A "contribution" is the original software, or any additions or changes to the software.
 *
 * A "contributor" is any person that distributes its contribution under this license.
 *
 * "Licensed patents" are a contributor's patent claims that read directly on its contribution.
 *
 * 2. Grant of Rights
 *
 * (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
 * each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, 
 * prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
 *
 * (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
 * each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, 
 * use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the 
 * contribution in the software.
 *
 * 3. Conditions and Limitations
 *
 * (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
 *
 * (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, 
 * your patent license from such contributor to the software ends automatically.
 *
 * (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, 
 * and attribution notices that are present in the software.
 *
 * (D) If you distribute any portion of the software in source code form, you may do so only under this license by including 
 * a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, 
 * you may only do so under a license that complies with this license.
 *
 * (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. 
 * You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, 
 * the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 
 */
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Compression;
using System.IO;
using RealmMudLib.Encryption;

namespace RelmMudServer.TCP
{
    public class Parser
    {
        static Random rnd = new Random();


        public static void Parse(Client cli, byte[] pak, int pid)
        {
            switch (pid)
            {
                case 10:
                    {
                        Console.WriteLine("Packet 1 recieved.");
                        // Send keys
                        byte[] keys = Parser.BuildPacket(cli, 10);
                        cli.Send(ref keys);
                    }
                    break;
                case 11:
                    {
                        string reply = Encoding.ASCII.GetString(pak);
                        if (reply == "KEYSRECEIVED")
                        {
                            // Then are keys are set up right.
                            // Ask client to log in.
                            byte[] login = Parser.BuildPacket(cli, 12);
                        }
                    }
                    break;
                case 12:
                    {
                        // Login packet from server
                        string login = Encoding.ASCII.GetString(pak);
                        // Break the username and password aprat
                        string user = login.Split(':').First();
                        string pass = login.Split(':').Last();
                        Console.WriteLine("Username: " + user + " Pass: " + pass);
                        /////////////////////////////////////////////////////
                        // For now just say ok, TODO hook this to mysql. 
                        byte[] reply = Parser.BuildPacket(cli, 14);
                        cli.Send(ref reply);
                        ///////////////////////////////
                    }
                    break;
                default:
                    Console.WriteLine("WorldServer: Unknown message (0x{0:X8}) or {1}", pid, pid);
                    Console.WriteLine(BitConverter.ToString(pak).Replace("-", ""));
                    //Console.WriteLine("Sending back anyway.");
                    //cli.Send(ref pak);
                    break;
            }
        }
        /// <summary>
        /// Generates a random string with the given length
        /// </summary>
        /// <param name="size">Size of the string</param>
        /// <param name="lowerCase">If true, generate lowercase string</param>
        /// <returns>Random string</returns>
        public static string RandomString(int size, bool lowerCase)
        {
            StringBuilder builder = new StringBuilder();
            Random random = new Random();
            char ch;
            for (int i = 0; i < size; i++)
            {
                ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                builder.Append(ch);
            }
            if (lowerCase)
                return builder.ToString().ToLower();
            return builder.ToString();
        }
        /// <summary>
        /// returns a ranom number string
        /// </summary>
        /// <param name="size">How many numbers in the string.</param>
        /// <returns></returns>
        public static string RandomNumberString(int size)
        {
            int num = 0;
            StringBuilder list = new StringBuilder();

            for (int i = 0; i < size; i++)
            {
                num = rnd.Next(0, 9);
                list.Append(num);

            }
            return list.ToString();
        }
        public static byte[] BuildPacket()
        {
            return new byte[4];
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pckID"></param>
        /// <returns></returns>
        public static byte[] BuildPacket(Client cli, int pckID)
        {
            // we can have 99 different packets, we dont need that many.
            List<byte> pckBuilder = new List<byte>();

            switch (pckID)
            {
                case 10:
                    {
                        // Hello packet, we send encryption keys here
                        // pkt id

                        pckBuilder.AddRange(BitConverter.GetBytes(pckID));//Encoding.ASCII.GetBytes(pckID.ToString()));
                        //pckBuilder.Add(0); // 1-9 packet ids need a blank byte
                        // generate keys to use for this client
                        cli.encryptionKey = Encoding.ASCII.GetBytes(Parser.RandomString(16, false));
                        cli.IVKey = Encoding.ASCII.GetBytes(Parser.RandomNumberString(16));
                        // Add the keys seperated by :
                        string keys = Encoding.ASCII.GetString(cli.encryptionKey) + ":" + Encoding.ASCII.GetString(cli.IVKey);
                        // turn keys into bytes
                        byte[] bkeys = Encoding.ASCII.GetBytes(keys);
                        // add to our packet
                        pckBuilder.AddRange(bkeys);
                        // create our password for encryption
                        string pass = "#UgMugaBugStug483#";
                        // Encrypt
                        try
                        {
                            byte[] encrypted = EncDec.Encrypt(pckBuilder.ToArray(), pass);
                            // Compress this packet
                            //byte[] compressed = CompressBytes(encrypted);
                            pckBuilder.Clear();
                            pckBuilder.AddRange(encrypted);
                            byte[] h = EncDec.Decrypt(encrypted, pass);
                            string bj = Encoding.ASCII.GetString(h);
                            Console.WriteLine(bj + " Length of packet" + pckBuilder.Count);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.ToString());
                        }

                    }
                    break;
                case 12:
                    {
                        // 12 is login request packet.
                        string loginRequest = "12LOGINPLEASE";
                        byte[] tobytes = Encoding.ASCII.GetBytes(loginRequest);
                        if (cli.encryptionKey.Length == 0)
                        {
                            Console.WriteLine("Blank Keys.");
                        }
                        else
                        {
                            byte[] reply = EncDec.Encrypt(tobytes, cli.encryptionKey, cli.IVKey);
                            cli.Send(ref reply);
                        }
                    }
                    break;
                case 13: // Failed login responce.
                    {
                        string badLogin = "13LOGINISBAD";
                        byte[] reply = Encoding.ASCII.GetBytes(badLogin);
                        pckBuilder.AddRange(EncDec.Encrypt(reply, cli.encryptionKey, cli.IVKey));
                        //cli.Send(ref reply);
                    }
                    break;
                case 14: // 14 is a OK responce to user and pass
                    {
                        string goodLogin = "14LOGINISGOOD";
                        byte[] reply = Encoding.ASCII.GetBytes(goodLogin);
                        pckBuilder.AddRange(EncDec.Encrypt(reply, cli.encryptionKey, cli.IVKey));
                        //cli.Send(ref reply);
                    }
                    break;
                default:
                    Console.WriteLine(" Unknown Packet build request (" + pckID + ")");
                    break;
            }
            return pckBuilder.ToArray();
        }
        public static void ParseEncryptedPacket(Client cli, byte[] pak)
        {
            Dictionary<int, byte[]> tmp = new Dictionary<int, byte[]>();
            try
            {
                byte[] unEncrypted = EncDec.Decrypt(pak, cli.encryptionKey, cli.IVKey);
                // Get the Id
                byte[] id = new byte[4];
                Array.Copy(unEncrypted, id, 4);
                int pkt_id = BitConverter.ToInt32(id, 0);//int.Parse(Encoding.ASCII.GetString(id));
                switch (pkt_id)
                {
                    case 11:
                        {
                            Console.WriteLine("We got a keys recieved packet");
                            // Lets format any reply for now
                            byte[] pckId = BitConverter.GetBytes(12); // Packet id 12
                            byte[] reply = Encoding.ASCII.GetBytes("OK");
                            List<byte> packet = new List<byte>();
                            packet.AddRange(pckId);
                            packet.AddRange(reply);

                            byte[] final = EncDec.Encrypt(packet.ToArray(), cli.encryptionKey, cli.IVKey);
                            cli.Send(ref final);
                        }
                        break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }
}
